package cn.cxyxj;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
public class HelloController {

    private static final String GET_RESOURCE = "get";

    @RequestMapping("/get-1")
    public String get1(){
        List<FlowRule> rules = new ArrayList<>();
        //  定义流控规则
        FlowRule rule = new FlowRule();
        // 设置需要被流控的资源名称，一般是接口路径。需要与定义的资源名称一致
        rule.setResource("/get-1");
        //设置流控规则-QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护资源阈值-每秒只能访问一次
        rule.setCount(1);
        rules.add(rule);
        //加载配置好的规则
        FlowRuleManager.loadRules(rules);
        return "hello world";
    }

    @RequestMapping("/get-2")
    public String get2() throws InterruptedException {
        List<FlowRule> rules = new ArrayList<>();
        //  定义流控规则
        FlowRule rule = new FlowRule();
        // 设置需要被流控的资源名称，一般是接口路径。需要与定义的资源名称一致
        rule.setResource("/get-2");
        //设置流控规则-QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护资源阈值-每秒只能访问一次
        rule.setCount(1);
        rules.add(rule);
        //加载配置好的规则
        FlowRuleManager.loadRules(rules);
        Thread.sleep(999);
        // 1.5.0 版本开始可以直接利用 try-with-resources 特性
        try (Entry entry = SphU.entry("/get-2")) {
            // 被保护的逻辑
            System.out.println("hello world");
            return "hello world";
        } catch (BlockException ex) {
            ex.printStackTrace();
            // 处理被流控的逻辑
            System.out.println("blocked!");
            return "被流控";
        }
    }

    @RequestMapping("/get")
    public String get() {
        // 配置规则.
        initFlowRules();

        // 1.5.0 版本开始可以直接利用 try-with-resources 特性
        try (Entry entry = SphU.entry(GET_RESOURCE)) {
            // 被保护的逻辑
            System.out.println("hello world");
            return "hello world";
        } catch (BlockException ex) {
            // 处理被流控的逻辑
            System.out.println("blocked!");
            return "被流控";
        }
    }

    private static void initFlowRules() {

        List<FlowRule> rules = new ArrayList<>();
        //  定义流控规则
        FlowRule rule = new FlowRule();
        // 设置需要被流控的资源名称，一般是接口路径。需要与定义的资源名称一致
        rule.setResource(GET_RESOURCE);
        //设置流控规则-QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //设置受保护资源阈值-每秒只能访问一次
        rule.setCount(1);
        rules.add(rule);
        //加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }

    @RequestMapping("/get3")
    //@SentinelResource(blockHandler = "exceptionHandler")
    public String get3(String name) {
        // 配置规则.
        initDegradeRule();
        // 被保护的逻辑
        int i = 10/0;
        return "hello world" + name;
    }

    /**
     * 熔断降级规则
     */
    private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        //设置熔断降级资源名
        degradeRule.setResource("/get3");
        //设置降级规则-异常数
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        //阈值计数，这里是触发熔断异常数 大于2
        degradeRule.setCount(2);
        //可以触发熔断的最小请求数（在活动统计时间跨度内）2
        degradeRule.setMinRequestAmount(2);
        //统计时间间隔，单位ms 1分钟
        degradeRule.setStatIntervalMs(20*1000);
        //熔断器打开时的恢复超时（以秒为单位）。超时后，断路器将转换为半开状态以尝试一些请求
        degradeRule.setTimeWindow(10);

        rules.add(degradeRule);
        //加载配置好的规则
        DegradeRuleManager.loadRules(rules);

        //上述意思是在一分钟内，请求数超过2次并且当异常数大于2（第三次也算入异常，第四次才熔断），
        // 之后请求会被熔断；10s后断路器转换为半开状态，当再次请求又异常时，下次会直接被熔断，之后重复（10s-半开-请求）。
    }

    @RequestMapping("/get2")
    @SentinelResource(value = "get2", blockHandler = "exceptionHandler", fallback = "helloFallback")
    public String get2(String name) {
        // 配置规则.
        initFlowRules();
        // 被保护的逻辑
        return "hello world" + name;
    }

    // Fallback 函数，函数签名与原函数一致或加一个 Throwable 类型的参数.
    public String helloFallback(String name,Throwable e) {
        return String.format("helloFallback %s", name);
    }

    // Block 异常处理函数，参数最后多一个 BlockException，其余与原函数一致.
    public String exceptionHandler(String name, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "exceptionHandler " + name;
    }
}
